package com.opc.client;

import com.opc.entity.DeviceRecord;
import com.opc.entity.DeviceRecordLatest;
import com.opc.mapper.DeviceRecordLatestMapper;
import com.opc.mapper.DeviceRecordMapper;
import com.opc.service.DeviceRecordService;
import com.opc.service.DeviceService;
import com.opc.utils.OpcClient;
import com.opc.utils.RedisUtils;
import com.opc.utils.Result;
import com.opc.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * code: https://blog.csdn.net/yhtppp/article/details/80680446
 * code: https://blog.csdn.net/Tuine/article/details/125894291
 * DCOM 配置 https://www.cnblogs.com/ioufev/p/9365919.html
 * TODO 设置值和计数值， 重置新增字段，上一次的记录值，列表显示 只显示最新的计数值和设置值
 * 计数值更新  设置值重新产生一条 用于异常分析 （离线分析）
 *
 * @author SEG
 */
@Component
public class OpcClientBean {

    @Resource
    private DeviceService deviceService;
    @Resource
    private DeviceRecordService recordService;

    @Resource
    private DeviceRecordMapper recordMapper;
    @Resource
    private DeviceRecordLatestMapper recordLatestMapper;
    @Resource
    private RedisUtils redisUtils;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public void start(String host, String user, String password, String domain, String clsid) {
        OpcClient opcClient = new OpcClient();
        // 1.显示server上的opc server应用列表
//        opcClient.showAllOPCServer(host, user, password, domain);
        // 1.连接指定的opc server
        boolean ret = opcClient.connectServer(host, clsid, user, password, domain);
        if (!ret) {
            logger.error("connect opc server fail, opc server may not start, please check it in time");
            return;
        }
        List<String> itemIdList = new ArrayList<>();
        // 2.检查opc server上的检测点 TODO 正式使用
        List<Object> keyList = redisUtils.lRange("key_list", 0, -1);
        keyList.forEach(key -> itemIdList.add(String.valueOf(key)));
//        itemIdList.add("MWSMART.PVS1.PVS1_FA1320_01_SET");
        ret = opcClient.checkItemList(itemIdList);
        if (!ret) {
            logger.error("not contain check item list, please check the opc server check item");
            return;
        }
        // 3.注册回调
        opcClient.subscribe((observable, arg) -> {
            Result result = (Result) arg;
            logger.info("update result:{}", result);
            String deviceKey = result.getItemId();
            Integer curValue = Integer.parseInt(String.valueOf(result.getValue()));
            String[] line_array = new String[0];
            if (!StringUtils.isBlank(deviceKey)) {
                line_array = deviceKey.split("\\.");
            }
            if (curValue == -1) {
//                redisUtils.set(line_array[1], 0);
                return;
            }
//            else {
////                redisUtils.set(line_array[1], 1);
////            }
            String deviceCode = (String) redisUtils.hmGet(deviceKey, "device_code");
            Integer val = (Integer) redisUtils.hmGet(deviceKey, "val");
            Integer type = (Integer) redisUtils.hmGet(deviceKey, "type");
            Object recordRedis = redisUtils.hmGet(deviceKey, "recordId");
            Object recordLatestRedis = redisUtils.hmGet(deviceKey, "recordLatestId");
            Long recordId = null;
            Long recordLatestId = null;
            if (recordRedis != null) {
                recordId = Long.parseLong(recordRedis + "");
            }
            if (recordLatestRedis != null) {
                recordLatestId = Long.parseLong(recordLatestRedis + "");
            }
            //1.更换就是重置次数
            if (recordId != null) {
                //如果记录存在，说明数据已存在，判断是否更新数据
                //缓存中的上一个计数值
                if (val == null) {
                    //如果值为空，查找数据库，数据是否存在
                    DeviceRecord record = recordMapper.selectById(recordId);
                    if (record == null) {
                        redisUtils.hmSet(deviceKey, "val", 0);
                        logger.warn("data is not exist in database");
                    } else {
                        //赋值，并且写入redis
                        val = record.getVal();
                        redisUtils.hmSet(deviceKey, "val", val);
                    }
                } else {
                    if (curValue.equals(val)) {
//                        logger.info("设备id存在，两次计数一样不更新：{}", deviceCode); //TODO 测试
                        redisUtils.hmSet(deviceKey, "val", val);
                        //两次计数一样不更新
                    } else {
                        updateRecord(curValue, deviceKey, val, type, recordId, deviceCode, recordLatestId);
                    }
                }

            } else {
                insertRecord(curValue, deviceKey, deviceCode, val, type);
            }
        });
        // 4.添加监听检测点的数据可以访问
        for (String item : itemIdList) {
            opcClient.syncReadObject(item, 1000);
        }
        delay(2000);
    }

    private void insertRecord(Integer curValue, String device_key, String deviceCode, Integer val, Integer type) {
        DeviceRecord r = new DeviceRecord();
        DeviceRecordLatest deviceRecordLatest = new DeviceRecordLatest();
        r.setCreateTime(new Date());
        r.setIsDelete(0);
        r.setCreateUser(1);
        r.setDeviceCode(deviceCode);
        r.setVal(curValue);
        r.setValType(type);
        r.setResetTimes(0);
        //保存记录数据
        BeanUtils.copyProperties(r, deviceRecordLatest);
        int id = recordMapper.insert(r);
        int idLatest = recordLatestMapper.insert(deviceRecordLatest);
        if (id == 0 || idLatest == 0) {
            logger.error("save record is failure id:{},idlatest:{}", id, idLatest);
        } else {
            //更新redis的数据
            redisUtils.hmSet(device_key, "recordId", r.getRecordId());
            redisUtils.hmSet(device_key, "recordLatestId", deviceRecordLatest.getRecordId());
            redisUtils.hmSet(device_key, "val", val);
        }
    }

    private void updateRecord(Integer curValue, String deviceKey, Integer val, Integer type,
                              Long recordId, String deviceCode, Long recordLatestId) {
        DeviceRecord r = new DeviceRecord();
        DeviceRecord record = new DeviceRecord();
        DeviceRecordLatest deviceRecordLatest = new DeviceRecordLatest();
        Date nowTime = new Date();
        record.setValType(type);
        if (type == 1) {
            //如果是1 说明是计数值记录
            if (curValue == 0 && val != 0) {
                //如果发生重置，生成一条新的记录，redis中的记录id记为控制
                r.setCreateTime(nowTime);
                r.setDeviceCode(deviceCode);
                r.setCreateUser(1);
                r.setIsDelete(0);
                r.setVal(0);
                r.setValType(1);
                //保存记录数据
                int id = recordMapper.insert(r);
                if (id == 0) {
                    logger.error("create new record is failure");
                } else {
                    record.setValType(2);
                    record.setResetTime(nowTime);
                    record.setResetTimes(1);
                    redisUtils.hmSet(deviceKey, "recordId", r.getRecordId());
                    redisUtils.hmSet(deviceKey, "val", 0);
                }
            }
        }
        record.setRecordId(recordId);
        if (record.getValType() != 2) {
            val = curValue;
        }
        record.setVal(val);
        record.setUpdateTime(nowTime);
        deviceRecordLatest.setRecordId(recordLatestId);
        deviceRecordLatest.setVal(val);
        deviceRecordLatest.setUpdateTime(nowTime);
        int uLatestFlag = recordLatestMapper.updateById(deviceRecordLatest);
        int uFlag = recordMapper.updateById(record);
        if (uFlag == 0 || uLatestFlag == 0) {
            logger.error("update data is failure recordLastId:{}, recordId:{},devicecode:{},type:{},LuFlag:{},uLatestFlag:{},val:{},curVale:{}",
                    recordLatestId, recordId, deviceCode, type, uFlag, uLatestFlag, val, curValue);
        } else {
            redisUtils.hmSet(deviceKey, "val", curValue);
        }
    }

    private static void delay(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
